package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"math"
	"os"
	"os/exec"
)

type Selpg struct {
	start_page  int    //起始页数，从0开始计数
	end_page    int    //终止页数
	in_filename string //输入文件
	page_len    int    //每页输出的长度（即行数，当以-f打印时此值设定为-1
	page_type   bool   //打印文件的类型，其中true代表按分页符打印-f，false代表按行打印-l
	destination string //输出终端
}

var progname string

func usage() {
	fmt.Printf("Usage of %s:\n", progname)
	fmt.Printf("seplg -s num1 -e num2 [-f / -l num3] [-d str1] input_file\n")
	flag.PrintDefaults()
}

func process_args(selpg *Selpg) {
	//未设定起止页码
	if selpg.start_page == -1 || selpg.end_page == -1 {
		fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", progname)
		flag.Usage()
		os.Exit(1)
	}

	//起止命令输入错误
	if os.Args[1][0] != '-' || os.Args[1][1] != 's' {
		fmt.Fprintf(os.Stderr, "%s: 1st arg should be -s start_page\n", progname)
		flag.Usage()
		os.Exit(2)
	}
	end_index := 2
	if len(os.Args[1]) == 2 {
		end_index = 3
	}

	if os.Args[end_index][0] != '-' || os.Args[end_index][1] != 'e' {
		fmt.Fprintf(os.Stderr, "%s: 2st arg should be -e end_page\n", progname)
		flag.Usage()
		os.Exit(2)
	}

	//起止页码过小
	if selpg.start_page < 0 || selpg.end_page < 0 {
		fmt.Fprintf(os.Stderr, "Invalid arguments\n")
		flag.Usage()
		os.Exit(3)
	}

	// 起止页码过大，溢出了
	if selpg.start_page > math.MaxInt32-1 || selpg.end_page > math.MaxInt32-1 {
		fmt.Fprintf(os.Stderr, "Invalid arguments\n")
		flag.Usage()
		os.Exit(4)
	}

	//起始页码大于结束页码
	if selpg.start_page > selpg.end_page {
		fmt.Fprintf(os.Stderr, "The start_page num is less than the end_page num\n")
		flag.Usage()
		os.Exit(5)
	}

	//-l和-f冲突
	if selpg.page_type == true && selpg.page_len != -1 {
		fmt.Fprintf(os.Stderr, "Conflict flags -f and -l\n")
		flag.Usage()
		os.Exit(6)
	}

	if selpg.page_len == -1 {
		selpg.page_len = 72
	}
}

func process_input(selpg *Selpg) {
	var stdin io.WriteCloser
	var err error
	var cmd *exec.Cmd

	//获得Stdin输入管道
	if selpg.destination != "" {
		cmd = exec.Command("cat", "-n")
		stdin, err = cmd.StdinPipe()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Can not obtain the stdin pipe for command.\n")
			os.Exit(7)
		}
	} else {
		stdin = nil
	}

	//打开输入文件
	if flag.NArg() > 0 {
		selpg.in_filename = flag.Arg(0)
		output, err := os.Open(selpg.in_filename)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Can not open the input file\n")
			os.Exit(8)
		}

		//读取输入文件内容并输出
		reader := bufio.NewReader(output)
		if selpg.page_type == true {
			//按分页符读取文件内容
			for pageNum := 0; pageNum <= selpg.end_page; pageNum++ {
				line, err := reader.ReadString('\f')
				if err != nil {
					if err == io.EOF {
						break
					}
					fmt.Fprintf(os.Stderr, "Failed to read '\f'\n")
					os.Exit(9)
				}

				printOrWrite(selpg, string(line), stdin)
			}
		} else {
			//按行数读取文件内容
			count := 0
			for {
				line, _, err := reader.ReadLine()

				if err != io.EOF && err != nil {
					fmt.Fprintf(os.Stderr, "Failed to read by line\n")
					os.Exit(10)
				}
				if err == io.EOF {
					break
				}
				if count/selpg.page_len >= selpg.start_page {
					if count/selpg.page_len > selpg.end_page {
						break
					} else {
						printOrWrite(selpg, string(line), stdin)
					}
				}
				count++
			}
		}
	} else {
		//终端输入
		selpg.page_len = 72
		scanner := bufio.NewScanner(os.Stdin)
		count := 0
		target := ""
		for scanner.Scan() {
			line := scanner.Text()
			line += "\n"
			if count/selpg.page_len >= selpg.start_page {
				if count/selpg.page_len <= selpg.end_page {
					target += line
				}
			}
			count++
		}
		printOrWrite(selpg, string(target), stdin)
	}

	if selpg.destination != "" {
		stdin.Close()
		cmd.Stdout = os.Stdout
		cmd.Run()
	}
}

func printOrWrite(selpg *Selpg, line string, stdin io.WriteCloser) {
	if selpg.destination != "" {
		stdin.Write([]byte(line + "\n"))
	} else {
		fmt.Println(line)
	}
}

func main() {
	progname = os.Args[0]
	var selpg Selpg

	flag.Usage = usage
	flag.IntVar(&(selpg.start_page), "s", -1, "Start page")
	flag.IntVar(&(selpg.end_page), "e", -1, "End page")
	flag.IntVar(&(selpg.page_len), "l", -1, "Line per page")
	flag.BoolVar(&(selpg.page_type), "f", false, " Page type")
	flag.StringVar(&(selpg.destination), "d", "", "Print destination")
	flag.Parse()

	process_args(&selpg)
	process_input(&selpg)
}
